package org.jivesoftware.spark.crowd.packet;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromMatchesFilter;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.PacketExtensionFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.spark.crowd.CrowdRecorder;
import org.jivesoftware.spark.crowd.CrowdsManager;
import org.jivesoftware.spark.crowd.Param;
import org.jivesoftware.spark.crowd.ui.IandAPanel;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.ui.ChatRoomNotFoundException;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;

public class CrowdsMessageManager implements PacketListener{
	/**
	 * singleton mode ,support only one service name.
	 */
	private static CrowdsMessageManager instance;
	private Connection conn;
	/**
	 * fullDomain is full name of the crowd service address,which contains service name and server domain.
	 * e.g : crowd.127.0.0.1
	 */
	
	private String crowdServiceName;
	private List<CrowdMessageProcesser> processers = new ArrayList<CrowdMessageProcesser>();
	public static CrowdsMessageManager getInstance(Connection connection,String serviceName){
		if(instance == null)
			instance = new CrowdsMessageManager(connection,serviceName);
		return instance;
	}
	/**
	 * 
	 * @return
	 */
	public static CrowdsMessageManager getInstance(){
		return instance;	
	}
	private CrowdsMessageManager(Connection connection,String serviceName){
		conn = connection;
		this.crowdServiceName = serviceName;
		init();
	}
	private void init(){
		IandAParser.getInstance();
		MParser.MParserProvider.getInstance();
		DParser.DParserProvider.getInstance();
		
		PacketFilter messageFilter = new PacketTypeFilter(Message.class);
		PacketFilter crowdFilter = new FromMatchesFilter(Param.getFullCrowdDomain());
		PacketFilter andFilter = new AndFilter(messageFilter,crowdFilter);
		
		conn.addPacketListener(this, andFilter);
		
		PacketFilter extensionFilter_i = new PacketExtensionFilter(crowdServiceName, Param.invite_NS);
		PacketFilter extensionFilter_a = new PacketExtensionFilter(crowdServiceName, Param.apply_NS);
		PacketFilter orFilter = new OrFilter(extensionFilter_i,extensionFilter_a);
		
		PacketListener iandaListener = new PacketListener(){
			@Override
			public void processPacket(Packet packet) {
												
				final Message ianda = (Message)packet;
				
				System.out.println(ianda.toXML());
				IandAParser.Extension extension_a = (IandAParser.Extension)ianda.getExtension(crowdServiceName, Param.apply_NS);
				IandAParser.Extension extension_i = (IandAParser.Extension)ianda.getExtension(crowdServiceName, Param.invite_NS);
				System.out.println("extension_a == null?  "+extension_a == null);
				System.out.println("extension_i == null?  "+extension_i == null);
				
				if(extension_a == null && extension_i == null){
					CrowdRecorder.record("receieved packet is neither invite nor apply.");
					return;
				}else if(extension_a != null && extension_i != null){
					CrowdRecorder.record("receieved packet is either invite or apply.");
					return;
				}
				IandAParser.Extension extension = extension_a == null?extension_i:extension_a;
				String crowdName = extension.getCrowdName();
				
				String xmlns = extension.getNamespace();
//				System.out.println(xmlns);
				final JPanel panel = new IandAPanel(ianda.getFrom(), ianda.getTo(), crowdName, xmlns);
//				System.out.println("panel created");				
				SwingUtilities.invokeLater(new Runnable(){
					@Override
					public void run() {
						try {
							System.out.println("create chat room...");
							ChatRoom cr =Param.getChatContainer().getChatRoom(StringUtils.parseBareAddress(ianda.getFrom()));
																	
							cr.getTranscriptWindow().addComponent(panel);
							 cr.scrollToBottom();
							
							panel.getParent().validate();
							panel.getParent().repaint();
							
						} catch (ChatRoomNotFoundException e) {
							ChatRoom room = new ChatRoomImpl(StringUtils.parseBareAddress(ianda.getFrom()),Param.getNickNameFromJID(ianda.getFrom()),Param.getNickNameFromJID(ianda.getFrom()));
							
							Param.getChatContainer().addChatRoom(room);
							Param.getChatContainer().activateChatRoom(room);		
							room.getTranscriptWindow().addComponent(panel);
							
							 room.scrollToBottom();
							
							panel.getParent().validate();
							panel.getParent().repaint();
						}						
					}					
				});
				
				
				
				
			}			
		};
		conn.addPacketListener(iandaListener,new AndFilter(messageFilter,orFilter));
		
		PacketFilter extensionFilter_d = new PacketExtensionFilter(crowdServiceName, Param.destroy_NS);
		PacketFilter andFilterForDestroy = new AndFilter(messageFilter,extensionFilter_d);
		PacketListener dListener = new PacketListener(){
			@Override
			public void processPacket(Packet packet) {
				Message m = (Message)packet;
				DParser ext =(DParser) m.getExtension(crowdServiceName, Param.destroy_NS);
				String crowdName = ext.getCrowdName();
				CrowdsManager.getInstance().removeCrowd(crowdName);
				
			}			
		};
		conn.addPacketListener(dListener, andFilterForDestroy);
		
	}
	/**
	 * 
	 * @param crowdName
	 * @param text
	 * @throws XMPPException
	 */
	public synchronized Message sendMessage(final String crowdName,String text) throws XMPPException{
		Message message = new Message();
		
		message.setFrom(Param.getBareAddress());
		message.setTo(Param.getFullCrowdDomain());
		message.setBody(text);
		message.setType(Message.Type.chat);
		message.addExtension(new PacketExtension(){
			
			@Override
			public String getElementName() {
				// TODO Auto-generated method stub
				return Param.CROWD_SERVICE_NAME;
			}

			@Override
			public String getNamespace() {
				// TODO Auto-generated method stub
				return Param.message_NS;
			}

			@Override
			public String toXML() {
				return PacketAssembler.getinstance().getMessagePacket(crowdName);				
			}
			
		});
		conn.sendPacket(message);
		return message;
	}
	public void sendMessage(String crowdName,Message message){
		
	}
	
	
	@Override
	public synchronized void processPacket(Packet packet) {
		Message message = null;
		if(packet instanceof Message)
			message = (Message)packet;
		for(CrowdMessageProcesser c : processers){
			c.receiveMessage(message);
		}
		
	}
	
	public void addMessageListener(CrowdMessageProcesser cmp){
		if(!processers.contains(cmp))
			processers.add(cmp);
	}
	
	public interface CrowdMessageProcesser{
		public void receiveMessage(Message m);
	}
	
	
	
	
}
